<template>
  <div class="pan-root">
    <div class="mask" v-show="show.mask" @click="clickMask">
      <div class="loading" v-show="show.loading">
        <DoubleHelix />
      </div>
      <div class="preview" @click.stop v-show="show.textPreview">
        <TextView :title="preview.title" :content="preview.textContent" />
      </div>
      <div class="preview" @click.stop v-show="show.unknownPreview">
        <UnknowView :title="preview.title" :blob="preview.rawContent" />
      </div>
      <div class="preview" @click.stop v-show="show.imagePreview">
        <ImageView :title="preview.title" :url="preview.imgUrl" />
      </div>

    </div>
    <div class="pan-content" :class="{'under-mask': show.mask}">
      <div class="title-bar" v-show="path">
        <div class="nav">
          <div class="nav-item" @click="cd('/')">
            <div class="nav-name">主目录</div>
          </div>
          <div class="nav-item" v-for="(item, index) in navList" :key="index" @click="cd(item.path)">
            <img class="icon" src="~/assets/iright.svg" alt="">
            <div class="nav-name">{{item.name}}</div>
          </div>
        </div>
        <div class="upload">
          <img class="icon" src="~/assets/iplus.svg" alt="" @click="clickUpload">
          <input ref="upload" type="file">
        </div>

      </div>
      <div class="file-list">
        <div class="file" v-for="(item, index) in fileList" :key="index" @click="clickFile(item)">
          <img class="icon" src="~/assets/idir.svg" v-show="item.is_dir" alt="">
          <img class="icon" src="~/assets/ifile.svg" v-show="!item.is_dir" alt="">
          <div class="file-detail">
            <div class="detail-line">
              <div class="file-name">{{item.name}}</div>
            </div>
            <div class="detail-line">
              <div class="file-time">{{getTime(item.time)}}&nbsp;</div>
              <div class="file-size">{{getSize(item.size)}}</div>
            </div>
          </div>

        </div>
      </div>


      <MusicPlayer :url="preview.musicUrl" v-show="show.musicPlayer" @ready='show.loading=false;show.mask=false' />
    </div>

  </div>
</template>

<script>
import moment from 'moment'
import DoubleHelix from '../components/DoubleHelix.vue'
import TextView from '../components/TextView.vue'
import UnknowView from '../components/UnknowView.vue'
import ImageView from '../components/ImageView.vue'
import MusicPlayer from '../components/MusicPlayer.vue'
import { socketUrl, webUrl, goGithubOAuth } from '../util'

export default {
  name: 'Pan',
  components: {
    DoubleHelix,
    TextView,
    UnknowView,
    ImageView,
    MusicPlayer,
  },
  data() {
    return {
      ws: null,
      cmd: '',
      path: '',
      saveName: '',
      fileList: [],
      cache: {},

      show: {
        mask: true,
        loading: true,
        textPreview: false,
        unknownPreview: false,
        imagePreview: false,
        musicPlayer: false,
      },
      preview: {
        title: '',
        textContent: '',
        rawContent: null,
        imgUrl: '',
        musicUrl: '',
      }
    }
  },
  computed: {
    title() {
      return this.path;
    },
    navList() {
      if (!this.path) {
        return ''
      }
      if (this.path == '/') {
        return []
      }
      let navList = []
      let nameList = this.path.split(/[/\\]/);
      let str = ''
      for (let i=1;i<nameList.length;i++) {
        str += `/${nameList[i]}`
        navList.push({
          name: nameList[i],
          path: str
        })
      }
      return navList
    }
  },
  methods: {
    clickMask() {
      if (this.show.loading) {
        return;
      }
      for (let k in this.show) {
        this.show[k] = false;
      }
    },
    getSize(bytes) {
      if (bytes < Math.pow(2,10)) {
        return `${bytes}B`
      }
      else if (bytes < Math.pow(2,20)) {
        return `${(bytes / Math.pow(2,10)).toFixed(2)}KB`
      }
      else if (bytes < Math.pow(2,30)) {
        return `${(bytes / Math.pow(2,20)).toFixed(2)}MB`
      }
      else if (bytes < Math.pow(2,40)) {
        return `${(bytes / Math.pow(2,30)).toFixed(2)}GB`
      }
      else if (bytes < Math.pow(2,50)) {
        return `${(bytes / Math.pow(2,40)).toFixed(2)}TB`
      }
      else {
        return '--'
      }
    },
    getTime(timestamp) {
      return moment(timestamp*1000).format('YYYY-MM-DD kk:mm:ss');
    },
    init() {
      return new Promise((res, rej) => {
        this.ws = new WebSocket(socketUrl)

        this.ws.addEventListener('message', (event) => {
          this.onRecv(event.data)
        });

        this.ws.addEventListener('open', (event) => {
          this.ws.send(localStorage.getItem('github_login_code'));
          res()
        });

      })
    },
    async onRecv(data) {
      if (typeof(data) == 'string') {
        if (data == '1') {
          goGithubOAuth()
        }
        console.log(data)
      }
      else {
        const cmdBin = data.slice(0, 64)
        const cmd = await this.cmdDecode(cmdBin)
        if (cmd == 'ls') {
          this.lsHandle(data.slice(64))
        }
        else if (cmd == 'cd') {
          this.lsHandle(data.slice(64))
        }
        else if (cmd == 'put') {
          this.lsHandle(data.slice(64))
        }
        else if (cmd == 'get') {
          this.getHandle(data.slice(64))
        }
        else if (cmd == 'get_url') {
          this.getUrlHandle(data.slice(64))
        }
        else {
          console.log('unknown command \'' + cmd + '\'')
        }
      }

    },
    async lsHandle(data) {
      let dir_data = JSON.parse(await data.text())
      this.path = dir_data.path

      let list = dir_data.file
      // console.log(list)
      list.sort((a, b) => {
        let res = 0
        if (a.is_dir) res -= 10;
        if (b.is_dir) res += 10;
        res -= a.name < b.name ? 1 : -1
        return res;
      })
      this.fileList = list

      this.show.mask = false;
      this.show.loading = false;

    },
    async getHandle(data) {
      this.show.loading = false

      if (this.saveName.match('.(txt|html)$')) {
        let txt = await data.text()
        this.show.textPreview = true;
        this.show.mask = true;
        this.preview.title = this.saveName
        this.preview.textContent = txt
      }
      else if (this.saveName.match('.(png|jpg|jpeg)$')) {
        let fr = new FileReader();
        fr.readAsDataURL(data);
        fr.onload = () => {
          this.preview.imgUrl = fr.result
          this.show.imagePreview = true;
          this.show.mask = true;
          this.preview.title = this.saveName
        }
      }
      else if (this.saveName.match('.(mp3)$')) {
        console.log('music')
        console.log(data)
        let fr = new FileReader();
        fr.readAsDataURL(data);
        fr.onload = () => {
          this.preview.musicUrl = "data:audio/mpeg3;"+fr.result
          this.show.musicPlayer = true;
          this.show.mask = false;
          this.preview.title = this.saveName
        }
      }
      else {
        this.show.unknownPreview = true;
        this.show.mask = true;
        this.preview.title = this.saveName
        this.preview.rawContent = data
      }
    },
    async getUrlHandle(data) {
      let url = `${webUrl}/${await data.text()}`

      if (this.saveName.match('.(txt|html)$')) {
        let res = await fetch(url)
        let txt = await res.text()
        this.show.mask = true;
        this.show.loading = false;
        this.show.textPreview = true;
        this.preview.title = this.saveName
        this.preview.textContent = txt
      }
      else if (this.saveName.match('.(png|jpg|jpeg)$')) {
        this.preview.imgUrl = url
        this.show.loading = false;
        this.show.imagePreview = true;
        this.show.mask = true;
        this.preview.title = this.saveName
      }
      else if (this.saveName.match('.(mp3)$')) {
        this.preview.musicUrl = url
        // this.show.loading = false;
        this.show.musicPlayer = true;
        // this.show.mask = false;
        this.preview.title = this.saveName
      }
      else {
        let res = await fetch(url)
        let blob = await res.blob()
        this.show.unknownPreview = true;
        this.show.loading = false;
        this.show.mask = true;
        this.preview.title = this.saveName
        this.preview.rawContent = blob
      }
    },
    cmdEncode(s) {
      const limit = 64;
      if (s.length > limit) {
        throw 'cmd head length out of range'
      }
      let res = []
      for (let i=0;i<s.length;i++) {
        res.push(s.charCodeAt(i))
      }
      for (let i=0;i<limit-s.length;i++) {
        res.push(0)
      }

      return new Uint8Array(res)
    },
    async cmdDecode(b) {
      let list = new Uint8Array(await b.arrayBuffer())
        let res = ''
        for (let x of list) {
          if (x == 0) {
            break
          }
          res += String.fromCharCode(x)
        }
        return res;
    },
    encode(s) {
      let res = []
      for (let i=0;i<s.length;i++) {
        res.push(s.charCodeAt(i))
      }

      return new Uint8Array(res)
    },
    cd(arg) {
      this.show.mask = true;
      this.show.loading = true;

      if (this.path == arg) {
        return
      }
      let cmd = this.cmdEncode('cd')
      let content = this.encode(arg)
      let a = Array.from(cmd).concat(Array.from(content))
      this.ws.send(new Uint8Array(a))
    },
    get(arg) {
      this.show.mask = true;
      this.show.loading = true;

      let cmd = this.cmdEncode('get')
      let content = this.encode(arg)
      let a = Array.from(cmd).concat(Array.from(content))
      this.ws.send(new Uint8Array(a))

      this.saveName = arg
    },
    getUrl(arg) {
      this.show.mask = true;
      this.show.loading = true;

      let cmd = this.cmdEncode('get_url')
      let content = this.encode(encodeURI(arg))
      let a = Array.from(cmd).concat(Array.from(content))
      this.ws.send(new Uint8Array(a))

      this.saveName = arg
    },
    async put(name, file) {
      let cmd_bin = this.cmdEncode('put')
      let name_bin = this.cmdEncode(name)
      let fr = new FileReader();
      fr.readAsArrayBuffer(file);
      fr.onload = () => {
        // console.log(fr.result)
        let a = Array.from(cmd_bin)
        a = a.concat(Array.from(name_bin))
        a = a.concat(Array.from(new Uint8Array(fr.result)))

        this.show.mask = true
        this.show.loading = true

        this.ws.send(new Uint8Array(a))
      }
    },
    clickBack() {
      this.cd('..')
    },
    clickFile(item) {
      if (item.is_dir) {
        this.cd(item.name)
      }
      else {
        this.getUrl(item.name)
      }
    },
    async clickUpload() {
      let fileNode = this.$refs.upload
      fileNode.click()
      fileNode.addEventListener('change', (e) => {
        let file = e.target.files[0]
        this.put(file.name, file)
      })
    }
  },
  mounted() {
    this.init().then(() => {
      this.ws.send(this.cmdEncode('ls'))
    })

    // setTimeout(() => {
    //   this.ls()
    // }, 2000)

  }
}
</script>

<style scoped>
.pan-root {
  background-color: #fff;
  min-height: 100vh;
  font-size: 16px;
  position: relative;
}
.pan-content {
  height: 100vh;
  overflow: hidden;
}
.mask {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  /* background-color: #00000044; */
  display: flex;
  align-items: center;
  justify-content: center;
}
.under-mask {
  filter: blur(11px);
}
.loading {
  position: fixed;
  bottom: 50%;
  display: flex;
}
.title-bar {
  height: 60px;
  width: 100%;
  background-color: #fff;
  color: #000;
  display: flex;
  align-items: center;
  padding: 0 16px;
  border-bottom: 1px solid #f5f5f5;
}
.file-list {
  height: calc(100vh - 60px);
  overflow: auto;
}
.root-alias {
  margin-left: 8px;
}
.nav {
  margin-left: 8px;
  display: flex;
  align-items: center;
}
.nav-item {
  display: flex;
  align-items: center;
}
.file {
  display: flex;
  align-items: center;
  height: 60px;
  margin: 0 16px;
  border-bottom: 1px solid #fafafa;
}
.icon {
  height: 32px;
  width: 32px;
}
.file-detail {
  margin-left: 16px;
}
.detail-line {
  display: flex;
}
.file-name {
  color: #000;
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 4px;
  overflow: auto;
  white-space: nowrap;
  max-width: 200px;
}
.file-time {
  color: #A0A0A0;
  font-size: 12px;
}
.file-size {
  color: #A0A0A0;
  font-size: 12px;
}

.preview {
  margin: 20px;
  flex: 1;
  display: flex;
  align-items: stretch;
  min-height: 200px;
  max-height: 80vh;
  width: calc(100vw - 40px);
}

.upload {
  margin-left: auto;
  display: flex;
  align-items: center;
}
.upload input[type=file] {
  display: none;
}
</style>
